package com.atguigu.spring.aspect;

import com.atguigu.spring.annotation.AtguiguDoSth;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Order(5)
@Aspect // 声明当前类是一个切面类
@Component // 切面类也必须放入 IoC 容器
public class LogAspect {

    // 前置通知
    // @Before 注解：把当前方法标记为前置通知
    // value 属性：指定切入点表达式
    // 切入点表达式：限定具体哪一个（哪些）目标方法应用这个前置通知
    @Before(value = "com.atguigu.spring.pointcut.MyPointCut.pointCutExpression()")
    public void coreBeforePrintLog(JoinPoint joinPoint) {

        // ---------------------------------------------------
        // 获取目标方法上，注解中的属性值
        MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
        AtguiguDoSth annotation = methodSignature.getMethod().getAnnotation(AtguiguDoSth.class);
        String info = annotation.info();
        System.out.println("目标方法上注解属性值 info = " + info);
        // ---------------------------------------------------

        // 1、通过 JoinPoint 对象获取目标方法的签名
        // 签名：包含目标方法声明时所有信息，通过签名可以精确的定位到目标方法
        Signature signature = joinPoint.getSignature();

        // 2、通过签名对象获取目标方法的方法名
        String methodName = signature.getName();

        // 3、通过 JoinPoint 对象获取外界调用目标方法时传入的实参数组
        Object[] args = joinPoint.getArgs();

        System.out.println("[切面][前置通知][" + methodName + "方法开始][实参列表：" + Arrays.asList(args) + "]");
    }

    // 返回通知
    @AfterReturning(
            value = "com.atguigu.spring.pointcut.MyPointCut.pointCutExpression()",

            // 指定形参名称，这个形参用来接收目标方法的返回值
            returning = "returnValue")
    public void coreAfterSuccessPrintLog(JoinPoint joinPoint,

                                         // 接收目标方法的返回值
                                         Object returnValue) {

        Signature signature = joinPoint.getSignature();
        String methodName = signature.getName();

        System.out.println("[切面][返回通知][" + methodName + "方法成功][方法返回值：" + returnValue + "]");
    }

    // 异常通知
    @AfterThrowing(value = "com.atguigu.spring.pointcut.MyPointCut.pointCutExpression()",

            // 声明一个形参名称，这个形参用来接收目标方法抛出的异常
            throwing = "throwable")
    public void coreAfterFailedPrintLog(JoinPoint joinPoint,

                                        // 接收目标方法抛出的异常
                                        Throwable throwable) {

        Signature signature = joinPoint.getSignature();
        String methodName = signature.getName();

        System.out.println("[切面][异常通知][" + methodName + "方法失败][异常信息是：" + throwable.getMessage() + "]");
    }

    // 后置通知
    @After(value = "com.atguigu.spring.pointcut.MyPointCut.pointCutExpression()")
    public void coreAfterPrintLog(JoinPoint joinPoint) {

        Signature signature = joinPoint.getSignature();
        String methodName = signature.getName();

        System.out.println("[切面][后置通知][" + methodName + "方法结束]");
    }
}
